#include "play_worker.h"

void close_cb(uv_handle_t *handle)
{
  uv_async_t *async = reinterpret_cast<uv_async_t *>(handle);
  FrameCallbackData *data = reinterpret_cast<FrameCallbackData *>(async->data);
  delete data->mCallback;
  delete async->data;
  delete async;
}

void PlayWorker::Execute()
{
  SDL_LockMutex(mPlayer->mCtrlMutex);
  mPlayer->mPlayFlag = true;
  SDL_UnlockMutex(mPlayer->mCtrlMutex);
  while (!mPlayer->mReadEnd)
  {
    if (mPlayer->mStopFlag)
    {
      break;
    }
    int retval;
    SDL_LockMutex(mPlayer->mSeekMutex);
    retval = av_read_frame(mPlayer->mFmtCtx, mPlayer->mPacket);
    SDL_UnlockMutex(mPlayer->mSeekMutex);
    SDL_LockMutex(mPlayer->mCtrlMutex);
    printf("%d\n", retval);
    mPlayer->mReadEnd = (retval == AVERROR_EOF);
    SDL_UnlockMutex(mPlayer->mCtrlMutex);
    if (mPlayer->mReadEnd)
    {
      break;
    }
    else if (retval < 0)
    {
      continue;
    }
    if (mPlayer->mPacket->stream_index == mPlayer->mAudioIdx)
    {
      int ret = avcodec_send_packet(mPlayer->mDecoderCtx, mPlayer->mPacket);
      while (ret >= 0)
      {
        SDL_LockMutex(mPlayer->mCtrlMutex);
        ret = avcodec_receive_frame(mPlayer->mDecoderCtx, mPlayer->mFrame);
        SDL_UnlockMutex(mPlayer->mCtrlMutex);
        if (ret < 0)
          continue;
        // printf("time = %f - total = %f\n",
        //        mPlayer->mFrame->pts * av_q2d(mPlayer->mFmtCtx->streams[mPlayer->mAudioIdx]->time_base),
        //        (double)mPlayer->mFmtCtx->duration / AV_TIME_BASE);
        double pos = mPlayer->mFrame->pts * av_q2d(mPlayer->mFmtCtx->streams[mPlayer->mAudioIdx]->time_base);
        if (pos >= 0)
        {
          mPlayer->mLastCurrentPos = pos;
        }
        uv_async_send(mPlayer->mFrameAsync);
        SDL_LockMutex(mPlayer->mCtrlMutex);
        int size = av_samples_get_buffer_size(NULL, 2, mPlayer->mFrame->nb_samples, AV_SAMPLE_FMT_FLT, 0);
        uint8_t *buff = (uint8_t *)av_malloc(size);
        int sp = swr_convert(mPlayer->mSwr, &buff, size, (const uint8_t **)mPlayer->mFrame->data, mPlayer->mFrame->nb_samples);
        float **origin = (float **)malloc(sizeof(float *) * 2);
        origin[0] = (float *)malloc(sizeof(float) * sp);
        origin[1] = (float *)malloc(sizeof(float) * sp);
        for (int i = 0; i < sp; i++)
        {
          origin[0][i] = *((float *)(buff) + i * 2);
          origin[1][i] = *((float *)(buff) + i * 2 + 1);
        }
        av_free(buff);
        mPlayer->mStretcher->process((const float *const *)origin, sp, false);
        int availLen = mPlayer->mStretcher->available();
        float **after = (float **)malloc(sizeof(float *) * 2);
        after[0] = (float *)malloc(sizeof(float) * availLen);
        after[1] = (float *)malloc(sizeof(float) * availLen);
        memset(after[0], 0, sizeof(float) * availLen);
        memset(after[1], 0, sizeof(float) * availLen);
        mPlayer->mStretcher->retrieve((float *const *)after, availLen);
        SDL_UnlockMutex(mPlayer->mCtrlMutex);
        if (!mPlayer->mBufferLocked)
        {
          SDL_LockMutex(mPlayer->mBufferMutex);
          mPlayer->mBufferLocked = true;
        }
        if (!mPlayer->mOverflow)
        {
          if (mPlayer->mLeftCount > 0)
          {
            for (int i = 0; i < mPlayer->mLeftCount; i++)
            {
              mPlayer->setPcmBuf(1, i, mPlayer->mLeftBuf[0][i]);
              mPlayer->setPcmBuf(2, i, mPlayer->mLeftBuf[1][i]);
            }
          }
          mPlayer->mOffset = mPlayer->mLeftCount;
        }
        if (availLen + mPlayer->mOffset < mPlayer->mSamplePerFrame)
        {
          for (int i = mPlayer->mOffset; i < availLen + mPlayer->mOffset; i++)
          {
            mPlayer->setPcmBuf(1, i, after[0][i - mPlayer->mOffset]);
            mPlayer->setPcmBuf(2, i, after[1][i - mPlayer->mOffset]);
          }
          mPlayer->mOffset += availLen;
          mPlayer->mOverflow = true;
        }
        else
        {
          mPlayer->mOverflow = false;
          int t = mPlayer->mSamplePerFrame - mPlayer->mOffset;
          for (int i = 0; i < t; i++)
          {
            mPlayer->setPcmBuf(1, i + mPlayer->mOffset, after[0][i]);
            mPlayer->setPcmBuf(2, i + mPlayer->mOffset, after[1][i]);
          }
          mPlayer->mFreshBuf = true;
          SDL_UnlockMutex(mPlayer->mBufferMutex);
          mPlayer->mBufferLocked = false;
          SDL_LockMutex(mPlayer->mCondMutex);
          SDL_CondWait(mPlayer->mCond, mPlayer->mCondMutex);
          availLen -= t;
          int cycle = 0;
          while (availLen >= mPlayer->mSamplePerFrame)
          {
            for (int i = 0; i < mPlayer->mSamplePerFrame; i++)
            {
              mPlayer->setPcmBuf(1, i, after[0][t + i + cycle * mPlayer->mSamplePerFrame]);
              mPlayer->setPcmBuf(2, i, after[1][t + i + cycle * mPlayer->mSamplePerFrame]);
            }
            mPlayer->mFreshBuf = true;
            SDL_UnlockMutex(mPlayer->mBufferMutex);
            mPlayer->mBufferLocked = false;
            SDL_LockMutex(mPlayer->mCondMutex);
            SDL_CondWait(mPlayer->mCond, mPlayer->mCondMutex);
            availLen -= mPlayer->mSamplePerFrame;
            cycle++;
          }
          mPlayer->mLeftCount = availLen;
          for (int i = 0; i < availLen; i++)
          {
            mPlayer->mLeftBuf[0][i] = after[0][t + i + cycle * mPlayer->mSamplePerFrame];
            mPlayer->mLeftBuf[1][i] = after[1][t + i + cycle * mPlayer->mSamplePerFrame];
          }
        }

        free(origin[0]);
        free(origin[1]);
        free(origin);
        free(after[0]);
        free(after[1]);
        free(after);
      }
    }
    av_packet_unref(mPlayer->mPacket);
  }
  SDL_LockMutex(mPlayer->mCtrlMutex);
  mPlayer->mPlayEndFlag = true;
  SDL_UnlockMutex(mPlayer->mCtrlMutex);
  mPlayer->mLastCurrentPos = (double)mPlayer->mFmtCtx->duration / AV_TIME_BASE;
  uv_async_send(mPlayer->mFrameAsync);
  uv_mutex_t mutex;
  uv_mutex_init(&mutex);
  uv_mutex_lock(&mutex);
  uv_cond_wait(mPlayer->mLastFrameConduv, &mutex);
  uv_close(reinterpret_cast<uv_handle_t *>(mPlayer->mFrameAsync), close_cb);
  mPlayer->mFrameAsync = NULL;
  SDL_UnlockMutex(mPlayer->mBufferMutex);
}

void PlayWorker::Destroy(){
  mPlayer->UnrefPlayer();
  delete this;
}